#include "lib.h"

#include "bjhand.h"
#include "bjgame.h"
#include "bjbet.h"
#include "bjconn.h"
#include "bjshoe.h"
#include "cardmgr.h"
#include "bjsaved.h"
#include "bjrules.h"
#include <ctype.h>
#include "cardset.h"
#include "PerfectPairSideBet.h"

static CardMgr stdMgr;

//
// balance is included in EVERY message
//

#if (DB_FORCEDHANDS)

uint8 forcedHand [16] [64] =
{
	{	0					}	//0 RANDOM
};

int dbForceDealerHand = 0;
int dbForcePlayerHand = 0;

// Quick hack to improve the behavior of the forced hand code
// wrt split hands (the hands need to be joined together)
// Note that it's completely thread-unsafe
int numDealerCards    = 0;
int numPlayerCards    = 0;
int dealerPosition    = 0;
int playerPosition    = 0;
    

void SetForcedHand( int n, char* pattern )
{
	if (pattern == NULL)
		return;
	if (n < 0)
		return;
	if (n > 2)
		return;

	int slen = strlen(pattern);

	int k=0;
	for (int i=0; i<slen; i+=2)
	{
		if (k >= sizeof(forcedHand[0]))
			break;
		forcedHand[n][k++] = GetCardFromPattern( pattern[i], pattern[i+1] );
	}

	if (n == 1)
		numDealerCards = k;
	if (n == 2)
		numPlayerCards = k;
}

void DBLoadForcedHands( BJConnection* conn )
{
	//zero out the forced hands
	memset( forcedHand, 0, sizeof(forcedHand) );

	//default is not forced
	dbForceDealerHand = 0;
	dbForcePlayerHand = 0;

	char	dealer[64];
	char	player[64];
	memset(dealer,0,sizeof(dealer));
	memset(player,0,sizeof(player));
	conn->trackGetForcedHands
	(
		dealer,
		player,
		64
	);

	if (dealer[0])
	{
		SetForcedHand( dbForceDealerHand=1, dealer );
	}

	if (player[0])
	{
		SetForcedHand( dbForcePlayerHand=2, player );
	}
}

#else

void DBLoadForcedHands( BJConnection* conn )
{
	//LOAD NOTHING
}

#endif



BJGame::BJGame(BJConnection* conn, BJRules* pRules, int32* bets, int32* side_bets)
{
	this->conn			= conn;
	m_pRules			= pRules;

	bool8 isHardAces	= IsHardAces();

	int32 nDecks		= pRules->GetNDecks();

	ShueGameInfo	sgi;
	sgi.gameId = GAMEID;
	sgi.machId = conn->GetMachId();
    #ifdef FORCEDSHOE 
       sgi.pid=conn->GetPID();
	#endif

	shue				= new BJShoe(&stdMgr, nDecks, sgi);
	shue->shuffle();

    // kharmon.  10-23-06.  #16151.  Save off the deck before any cards are dealt.
    // We'll need this info to restore the dealer cards.
    shue->save(&m_originalShoe);

	dealer				= new BJHand(&stdMgr,isHardAces);

	m_maxBJBets = pRules->GetMaxBJBets();

    // CSN-10574 - Initialize bets
	memset(bjBet, 0, sizeof(bjBet));
    m_currentBJBet = (pBJBet)0;
    m_nBJBets = 0;

    initializeSideBet(side_bets);

    for( int i=0; i<m_maxBJBets; i++ )
	{
        // create a new BJBet for each bet 
        if( bets[i] )
        {
		    BJHand* player = new BJHand(&stdMgr,isHardAces);
		    player->SetNBets(1);

            bjBet[m_nBJBets] = new BJBet(this,conn,pRules,dealer,player,bets[i],i);

            if( !m_currentBJBet )
                m_currentBJBet = bjBet[m_nBJBets];

            m_nBJBets++;
        }
		else
		{
			bjBet[m_nBJBets] = 0;			
		}
	}
	
	//set the deck filter (usually zero)
	shue->SetDeckFilter( m_pRules->GetDeckFilter() );

	//DEBUG
	DBLoadForcedHands( conn );
}


BJGame::BJGame(BJConnection* conn, BJRules* pRules, BJSavedGame* saved)
{
	this->conn			= conn;
	m_pRules			= pRules;

	bool8 isHardAces	= IsHardAces();

	ShueGameInfo	sgi;
	sgi.gameId = GAMEID;
	sgi.machId = conn->GetMachId();

    #ifdef FORCEDSHOE 
       sgi.pid=conn->GetPID();
	#endif

	shue = new BJShoe(&stdMgr, saved->numDecks, sgi);

	// restore dealer hand
	dealer = new BJHand(&stdMgr,isHardAces);

    initializeSideBet(saved);

    if(saved->bHasOriginalShoe)
    {
        // kharmon.  10-23-06.  #16151.  This is a newer saved game, where the 
        // dealer cards for an incomplete game aren't saved in the database.  
        // Use the original shoe settings to recreate the dealer cards.
        memcpy(&m_originalShoe, & saved->originalShoe, sizeof(m_originalShoe));
	    shue->restore(&saved->originalShoe);

	    //set the deck filter (usually zero)
	    shue->SetDeckFilter( m_pRules->GetDeckFilter() );
        
        uint8 *paucDealerCards = new uint8[saved->numDealerCards];
        for(int nI=0;nI<saved->numDealerCards;++nI)
        {
            paucDealerCards[nI]  = shue->getCard();
            paucDealerCards[nI] |= (saved->dealerCards[nI]&0x80);    // restore the face up bit using value from the database.
        }

        dealer->restore( paucDealerCards, saved->numDealerCards, false, 0, 0, (BJHand*)0 );

        delete [] paucDealerCards;
    }
    else
    {
        // kharmon.  10-23-06.  #16151.  This is an older saved game, use the old restore code.
	    dealer->restore( saved->dealerCards, saved->numDealerCards,	false, 0, 0, (BJHand*)0	);

        memset(&m_originalShoe, 0, sizeof(m_originalShoe));
    }

    // restore shue to the position it was at when the player exited the game.
	shue->restore(&saved->shue);

	//set the deck filter (usually zero)
	shue->SetDeckFilter( m_pRules->GetDeckFilter() );

	// CSN-10574 - Initialize bets
	memset(bjBet, 0, sizeof(bjBet));
	m_maxBJBets = saved->maxBJBets;
    m_nBJBets = saved->nBJBets;
    m_currentBJBet = (pBJBet)0;

	for( int i=0; i<m_nBJBets; i++ )
	{
        BJSavedGame::_BJBet* sBet = &(saved->BJBets[i]);

        // restore player hands
	    BJHand *player = NULL;
	    BJHand *prev = (BJHand*)0, *newhand;
	    for (int h = 0; h < sBet->numPlayerHands; h++)
	    {
		    newhand = new BJHand(&stdMgr,isHardAces);

		    if (!prev)
			    player = newhand;

		    newhand->restore( sBet->hands[h].cards,	sBet->hands[h].numCards,
			    sBet->hands[h].m_nBets,	sBet->hands[h].m_actionBits,
			    sBet->hands[h].bonusBalanceBet,	prev );

            // For each new hand that is restored, it should be safe to 
            //   assume that the initial player balance adjustment has
            //   taken place.
            newhand->balanceUpdated();

            prev = newhand;
	    }

        bjBet[i] = new BJBet(this,conn,pRules,dealer,player, saved, i );

        if( saved->currentBJBet == sBet->iBJBet )
        {
            m_currentBJBet = bjBet[i];
            m_currentBJBet->SetCurrentHandOffset( saved->currentHand );
        }
	}
	
	//DEBUG
	DBLoadForcedHands( conn );
}


BJGame::~BJGame()
{
	BJHand* temp;

	delete shue;

    // kharmon.  02-13-07.  #16101.
    delete m_pSideBet;
    m_pSideBet = NULL;

	while (dealer)
	{
		temp = dealer;
		dealer = (BJHand*)dealer->getNextHand();
		delete temp;
	}

	// delete BJBets
	for( int i=0; i<m_nBJBets; i++ )
	{
		delete bjBet[i];
		bjBet[i] = (pBJBet)0;
	}


}


void
BJGame::HandlePlayerAction()
{

    if( m_currentBJBet->IsDealerEnded() )
	{
		m_currentBJBet->HandlePlayerStand();
		return;
	}

    uint8 actionBits = m_currentBJBet->GetPlayerOptions();
	uint8 insurBtn = m_currentBJBet->GetInsuranceState() == BJBet::OFFERED;
    uint8 noinsBtn = 0;

    if( insurBtn  ) 
    {
        if( m_nBJBets > 1 )     // 2 bets: guide player through insurance procedure
        {                       // action buttons are disabled
            actionBits = 0;
            noinsBtn = 1;
        }
        else                    // single bet: Disable Surrender 
        {                       // to show player that we provide late surrender only!!
            if( actionBits & (1<<Surrender) )   //  
            {
                actionBits &= ~OPTION_FLAG(Surrender);
                noinsBtn = 1;
            }
        }
    }

	// else If the only choice is "Stand" and no Insurance offered --> finish it !!
	else if( 1<<Stand==actionBits )
	{
		m_currentBJBet->HandlePlayerStand();
		return;
	}

	conn->msgStart();
	conn->SENDBYTE('A');
	conn->SENDBYTE(actionBits);
	conn->SENDBYTE(insurBtn);	// show Insure button
	conn->SENDBYTE(noinsBtn);	// show small "No Insure" button
	conn->msgEnd();
}

void
BJGame::DealCard( BJHand* pHand, bool8 faceDown, int32 forceHand )
{
	//get one card from the shoe
	uint8 card = shue->getCard();

	#if (DB_FORCEDHANDS)

	if (forceHand == 1)		// Dealer
	{
		uint8 nextCard = forcedHand[forceHand][dealerPosition];
		if (nextCard)
			card = nextCard;

		++dealerPosition %= numDealerCards;
	}

	if (forceHand == 2)		// Player
	{
		uint8 nextCard = forcedHand[forceHand][playerPosition];
		if (nextCard)
			card = nextCard;

		++playerPosition %= numPlayerCards;
	}

	#endif

	pHand->add(card,faceDown);
}

void
BJGame::DealCardD( BJHand* pHand, bool8 faceDown )
{
	#if (DB_FORCEDHANDS)
        if( pHand == dealer )
		    DealCard( pHand, faceDown, dbForceDealerHand );
        else
		    DealCard( pHand, faceDown, dbForcePlayerHand );
	#else
		DealCard( pHand, faceDown );
	#endif
}


void
BJGame::firstDeal( char* pDealScript, int dealScriptLen, BJHand* hand, char who )
{
	for (int i=0; i<dealScriptLen; i++)
	{
		uint8 script = pDealScript[i];

		switch( script )
		{
		case 'd':
		case 'D':	//deal card to dealer
            if( who == 'd' )
			    DealCardD( hand, islower(script) );
			break;

		case 'p':
		case 'P':	//deal card to player
            if( who == 'p' )
			    DealCardD( hand, islower(script) );
			break;
		}
	}
}


bool8
BJGame::HandlePlayerDeal(int32 *pBonusBalanceBets)
{
    int32 i;
	int32 balance = 0;
	int32 total_bet = 0;    
   
    //get the deal script
	char	*pDealScript=NULL;
	int		dealScriptLen;
	int		canInsure;
	
	m_pRules->GetDealScript( &pDealScript, &dealScriptLen );

    // check if we can deal (each BJBet)
	for( i=0; i<m_nBJBets; i++ )
    {
        if( ! bjBet[i]->PlayerDeal() )
        {
            return false;
        }

		total_bet += bjBet[i]->GetBet();
    }

    // dealer's hand
    firstDeal( pDealScript,dealScriptLen, dealer, 'd' );
	canInsure = m_pRules->CanPlayerInsure(dealer);

    // players's hand(s)
	for( i=0; i<m_nBJBets; i++ )
	{
        pBJBet pb = bjBet[i];
        char *gid = pb->GetGID();    

        BJHand* hand = pb->getCurrentHand();
        firstDeal( pDealScript,dealScriptLen, hand, 'p' );

        if( canInsure ) 
			pb->SetInsuranceState(BJBet::OFFERED);

        // We updated the player's balance on the create game
        if( hand )
        {
            hand->balanceUpdated();
            hand->SetBonusBalanceBet(pBonusBalanceBets[i]);

            if(m_pSideBet!=NULL)
            {
                // kharmon.  02-13-07.  #16101.  Now that the Player cards are dealt, we have enough 
                // info to see whether the player won or lost their side bet.  This will also update
                // the database with the results.
                m_pSideBet->calculateDealResults(gid, hand, pb->GetBetOffset());
            }
        }

        // we need to create dealer record for each bet!!
        // this is intentional redundancy
        // each BJBet is represented by BJGame record and related dealer and player hands
        if( ! conn->trackDealerDeal(dealer,gid) ) 
			return false; 
        if( ! conn->trackPlayerDeal(hand,&balance,gid) ) 
			return false;
    }

	//when not saving fun play data, go ahead and deduct the
	//total bet from the account when the hand begins
	if(! conn->shouldSaveGameData())
	{		
        if(m_pSideBet!=NULL)
        {
            // Include the results of the sidebet in this balance update.
            for( int32 nI=0; nI<BJMAXBETS; ++nI )
            {
                int32 nPayout = m_pSideBet->getSideBetPayout(nI);

                if(nPayout==0)
                {
                    // player lost, so subtract the bet from his balance
                    total_bet += m_pSideBet->getSideBet(nI);
                }
                else
                {
                    // player won, just let him keep the bet, and give him the payout.
                    total_bet -= nPayout;
                }
            }
        }

        balance = conn->trackAdjustBalance(-total_bet);
	}

    //------------------------------------------------------------------
    // finaly send the deal message
    conn->msgStart();
    conn->SENDBYTE('D');
	conn->SENDINT4(balance);

    //deal script
    conn->SENDBYTE( dealScriptLen );
    for ( i=0; i<dealScriptLen; i++)
	    conn->SENDBYTE( pDealScript[i] );

    // send state
    sendState(false);    

    if(m_pSideBet!=NULL)
    {
        // kharmon.  02-13-07.  #16101.
        // okay, now that the Deal message has been sent, send down the 
        // message that tells the client about the side bet results. 
        m_pSideBet->sendResults();
    }

    conn->msgEnd();

	HandlePlayerNextAction(balance);

    return true;
}

bool8
BJGame::HandlePlayerNextAction(int32 balance)
{
	if (CheckEndOfGame())
		return true;

	return m_currentBJBet->HandlePlayerNextAction(balance);
}


bool8
BJGame::HandlePlayerHit(int32 balance)
{
	//insurance was not taken if it was available
	HandleInsuranceState();

    if (CheckEndOfGame())
		return true;
	return m_currentBJBet->HandlePlayerHit(balance);

}

bool8
BJGame::HandlePlayerStand()		// or bust
{
	//insurance was not taken if it was available
	HandleInsuranceState();

    if (CheckEndOfGame())
		return true;

	return m_currentBJBet->HandlePlayerStand();
}

bool8
BJGame::HandlePlayerDouble(int32 balance)
{
	//insurance was not taken if it was available
	HandleInsuranceState();

    if (CheckEndOfGame())
		return true;

	return m_currentBJBet->HandlePlayerDouble(balance);
}


bool8
BJGame::HandlePlayerSplit(int32 balance)
{
	//insurance was not taken if it was available
	HandleInsuranceState();

    if (CheckEndOfGame())
		return true;

	return m_currentBJBet->HandlePlayerSplit(balance);
}

void
BJGame::HandlePlayerPayout()
{
	m_currentBJBet->HandlePlayerPayout();
}

void
BJGame::HandleDealerResult()
{
	//what is the amount to be added/subtracted to this hand
	char* pPlayerBonusName=NULL;
	char* pDealerBonusName=NULL;
	m_pRules->GetPayout
	(
		1,
		dealer,
		dealer,
		1,
		&pPlayerBonusName,
		&pDealerBonusName
	);

	if (pDealerBonusName != NULL)
		dealer->SetBonusName( pDealerBonusName );
}

bool8
BJGame::HandlePlayerSurrender(int32 balance)
{
	//insurance was not taken if it was available
	HandleInsuranceState();

    if (CheckEndOfGame())
		return true;

	return m_currentBJBet->HandlePlayerSurrender(balance);
}

/// [2013-07-10] Charles. Added for supporting 'Zappit Rackcard'.
bool8 BJGame::HandlePlayerZap(int32 balance)
{
	//insurance was not taken if it was available
	HandleInsuranceState();

	if (CheckEndOfGame())
		return true;

	return m_currentBJBet->HandlePlayerZap(balance);
}

void
BJGame::sendState(bool8 bRestore)
{
	uint8*	iBet;
    uint8   j=0;

	//hard Aces
	conn->SENDBYTE( IsHardAces() );

    dealer->send( conn );

    // max number of bets 
    conn->SENDBYTE(m_maxBJBets);   

    // number of original bets 
    conn->SENDBYTE(m_nBJBets);   

    // current bet (subgame)
	iBet = conn->msgGetBuf();	// reserve 
	conn->msgReleaseBuf(1);

    // send state for all bets
	for(int i=0; i<m_nBJBets; i++ )
	{
        if( bjBet[i] == m_currentBJBet ) 
            j=m_currentBJBet->GetBetOffset();

        bjBet[i]->sendState();
    }

    if(bRestore==true && m_pSideBet!=NULL)
    {
        // kharmon.  02-13-07.  #16101.
        // also include the info for the perfect pair side bet results.
        m_pSideBet->sendSaveState();
    }

    *iBet = j;
}

int32
BJGame::GetCurrentBet()
{
	return m_currentBJBet->GetCurrentBet();
}

bool8
BJGame::IsHardAces()
{
	bool8 isHardAces = m_pRules->IsHardAces();
	return isHardAces;
}

int32
BJGame::GetBet()
{
	return m_currentBJBet->GetBet();
}



void
BJGame::save(BJSavedGame* saved, int8 nextBet)
{
    memcpy(&saved->originalShoe, &m_originalShoe, sizeof(saved->originalShoe)); // kharmon.  10-23-06.  #16151.  Save the original shoe settings.
    shue->save(&saved->shue);
	saved->numDecks	= shue->getNumDecks();
	saved->maxBJBets = m_maxBJBets;
    saved->nBJBets = m_nBJBets;

	if( nextBet >= 0 )		// we are switching to the next BJBet, so take it into account
	{
		saved->currentBJBet = nextBet;
		saved->currentHand  = 0; // CSN-10574
	}
	else
	{
		saved->currentBJBet = m_currentBJBet->GetBetOffset();
		saved->currentHand = m_currentBJBet->GetCurrentHandOffset();
	}

    // we need to get a gid for each BJBet !!
   	for(int i=0; i<m_nBJBets; i++ )
    {
        BJSavedGame::_BJBet *sb = &(saved->BJBets[i]);
        pBJBet              bet = bjBet[i];

        strcpy( sb->gid, bet->GetGID() );
        sb->m_insuranceState = bet->GetInsuranceState();

		if( nextBet == i )
			saved->currentHand = bet->GetCurrentHandOffset();
    }

}

bool8
BJGame::HandlePlayerInsurance(int32 sidebet, uint8 iBJBet)
{
	// validate iBJBet
    if( m_currentBJBet->GetBetOffset() != iBJBet )
        return false;

	// handle insurance request from the player
	if( ! m_currentBJBet->HandlePlayerInsurance(sidebet) ) 
        return false;


	// we may have another bet to insure !!
	pBJBet temp = nextBJBet();

    if( temp )
		m_currentBJBet = temp;

    // here we save the game with the new "m_insuranceState" 
    // and possible the new "m_currentBJBet"

    conn->trackSaveGame();

    if( temp )
	{
		// notify client to change active bet
	    conn->msgStart();
		conn->SENDBYTE('X');
		conn->SENDBYTE(m_currentBJBet->GetBetOffset() );
		conn->msgEnd();
        return true;
	}

    // all bets were processed - re-set "m_currentBJBet"
    // make sure that the m_currentBJBet points to the first (right) bet !!
    if( m_currentBJBet != bjBet[0] )
    {
		m_currentBJBet = bjBet[0];

		// notify client to change active bet
	    conn->msgStart();
		conn->SENDBYTE('X');
		conn->SENDBYTE(m_currentBJBet->GetBetOffset() );
		conn->msgEnd();
    }

	//check for payout
	HandleInsuranceState();

	// Here we know the player made insurance decision for
	// each BJBet - if dealer has BJ, we want to finish the game!!
	if( dealer->IsNatural() )
    {
        // dealer ended --> notify all BJBets
	    for(int i=0; i<m_nBJBets; i++ )
            bjBet[i]->SetDealerEnded();

		return m_currentBJBet->HandlePlayerStand();
    }

    return true;
}


/////////////////
void
BJGame::HandleDealer()
{
	bool8 isEOG = m_currentBJBet->EndOfTurnPlayer();
    pBJBet temp = nextBJBet();

    if ((!isEOG) || m_currentBJBet->GetInsuranceState() == BJBet::TAKEN)
    {
        if(conn->GetMachId() != EUROPEAN || nextBJBet() == NULL)    // kharmon.  08-13-07.  #19496.  For european blackjack, don't deal the dealer cards until after ALL player hands and cards have been dealt.
        {
		    while (HandleDealerNextAction(m_currentBJBet->getHands()));
        }
    }
    // ecarvalho. 06-09-08. FB #21762
    // For European BlackJack playing with 2 hands it is needed to guarantee the first hand insurance handling.
    // For some reason when playing with 2 hands and the first one accept insurance but the second one doesn't
    // and both hands get busted, player wins the insurance amount even the dealer doesn't get a BlackJack
    // Code below handle this special case
    //Start
    else if (isEOG && 2 == m_nBJBets && bjBet[0]->GetInsuranceState() == BJBet::TAKEN)
    {
        if(conn->GetMachId() == EUROPEAN)    
        {
		    while (HandleDealerNextAction(m_currentBJBet->getHands()));
        }
    }
    //End
    else if(m_nBJBets==2 && m_currentBJBet==bjBet[1] && bjBet[0]->EndOfTurnPlayer()==false)
    {
        // kharmon.  10-20-06.  #16151.  The player must have exited after finishing the
        // first hand and then re-entered to play the second hand, but then busted on the second hand.
        // SO, go ahead and make sure the dealer has played things out like he should have.
        // Note that this method can be called as many times as desired.  Once the dealer has stood,
        // calling it again doesn't do anything.
        while (HandleDealerNextAction(bjBet[0]->getHands()));
    }
	else if ( temp == NULL && // player has no more hands to play 
		conn->GetMachId() == EUROPEAN 
		&& dealer->getCount() == 1 // dealer have only 1 card
		&& ( dealer->GetNAces() == 1 || dealer->GetScore() == 10 ) ) // dealer can get blackjack
	{
		// osegura. 08/20/2008. FB 22886
		// Considering case for when the palyer gets blackjack right after the deal. The dealer
		// was keeping only 1 card and player always wins, now the player will deal the second card
		// to try to get a blackjack to make a push.
		if ( ( bjBet[0]->getHands() == 1 && bjBet[0]->getPlayerHand()->IsNatural() ) ||
			( 2 == m_nBJBets && bjBet[1]->getHands() == 1 && bjBet[1]->getPlayerHand()->IsNatural() ) )
		{
			HandleDealerNextAction(m_currentBJBet->getHands());
		}		
	}
// ecarvalho - 05-08-08 - FugBugz #21762 - Start
/*    else if(isEOG && temp==NULL && conn->GetMachId()==EUROPEAN)
    {
        // kharmon.  09-12-07.  #19617.  Because in Euro BJ the dealer only starts out with one card,
        // we need to make sure that the dealer's second card get's dealt here before actually ending the
        // game, even when the player busts.

        // ecarvalho - 05-08-08 - FugBugz #21762: Euro BJ: Dealer keeps drawing cards even after player has gone
        // Original code has been using the line below. The while statement makes server keep dealing cards
        // to the dealer until dealer gets some [17-21] hand or until dealer busts.
        // while(HandleDealerNextAction(bjBet[0]->getHands()));
        // Since the game is already over, there is no need for keeping dealing more cards, so
        // this 'else if' statement block was commented. It was not removed por document purpose only.
        // In the future if it is necessary to show the second dealers' card when player get busted
        // it could be done just calling the metohod below once, just removing its comment. 
        // HandleDealerNextAction(bjBet[0]->getHands());
    }
*/
// ecarvalho - 05-08-08 - FugBugz #21762 - End
    
    // we may have another bet to play !!	
    if (temp)
	{
		m_currentBJBet = temp;
		// notify client to change active bet
	    conn->msgStart();
		conn->SENDBYTE('X');
		conn->SENDBYTE(m_currentBJBet->GetBetOffset());
		conn->msgEnd();
	}
	else
	{
		HandleInsuranceState();
		EndOfGame();
	}
}

bool8
BJGame::HandleDealerNextAction( int32 nHands )
{
	int actionBits = m_pRules->GetDealerOptions( dealer,nHands);
	switch (actionBits)
	{

	case (1<<Hit):
		return HandleDealerHit();
		break;

	default:
	case (1<<Stand):
		return HandleDealerStand();
		break;

	}
}

bool8
BJGame::HandleDealerHit()
{
    if(conn->GetMachId() == EUROPEAN && dealer->getCount()==1)  // kharmon.  08-13-07.  #19496.  For Eurobj, the second card requires special handling because of the way it isn't dealt until later.
    {
	    DealCardD( dealer, true);
    }
    else
    {
        DealCardD( dealer, false);
    }

	return true;
}

bool8
BJGame::HandleDealerStand()
{
	return false;
}

bool8
BJGame::CheckEndOfGame( )
{
	bool8 eog = m_pRules->EndOfTurnDealer( dealer );

	if( !eog )
		return false;

    if( m_currentBJBet->GetInsuranceState() == BJBet::OFFERED ) 
		return false;

    // dealer ended --> notify all BJBets
	for(int i=0; i<m_nBJBets; i++ )
        bjBet[i]->SetDealerEnded();

    HandleDealer();  
	return true;
}



////////////////


pBJBet
BJGame::nextBJBet()
{
    bool8 next = false;

         
	for(int i=0; i<m_nBJBets; i++ )
	{
        if( next ) 
            return bjBet[i];

        else if( bjBet[i] == m_currentBJBet ) 
            next = true;
    }
    return (pBJBet)0;
}


void
BJGame::EndOfGame()
{
	int32	balance = 0;
    int32   i;

	dealer->RevealAllCards();

	//
	//	<msgsize>F<dealerHand>
	//		<num hands><hand payouts>
	//
	conn->msgStart();

	//tag
	conn->SENDBYTE('F');

	//dealer hand
	dealer->send( conn );
	conn->SENDBYTE(m_nBJBets);

    // for each BJBet 
    //      for each hand, send: payout, bonus & bonusName 
 	for( i=0; i<m_nBJBets; i++ )
        bjBet[i]->sendPayouts();
    

	//send a dealer payout record
    HandleDealerResult();
	conn->SENDINT4(0);
	conn->SENDINT4(0);
	conn->SENDNSTRING( dealer->GetBonusName() );

	conn->trackGetBalance(&balance);

	conn->SENDINT4(balance);
	conn->msgEnd();

    // if the player placed 2 wagers, we may have 2 game records in BJGames!!
    // this is intentional redundancy
    // each BJBet is represented by BJGame record and related dealer and player hands
 	for( i=0; i<m_nBJBets; i++ )
        conn->trackGameDone( dealer,  bjBet[i]->GetGID() );

	conn->gameDone(0);
}

char*
BJGame::GetGID( int32 iBJBet )
{ 
    return bjBet[iBJBet]->GetGID(); 
}


////////////////////////////////////////////////////////
bool8
BJGame::IsLateInsurance()
{
	int32 nDealerCards = dealer->getCount();
	
	if(conn->GetMachId()==EUROPEAN)
	{
		// kharmon.  01-16-08.  #20850.  The dealer only has 1 card to start 
		// with in european blackjack, so adjust the test accordingly.
		return nDealerCards<1;
	}
	else
	{
		return nDealerCards<=1;
	}
}

void
BJGame::HandleInsuranceState()
{

	for( int i=0; i<m_nBJBets; i++ )
	{
        pBJBet pb = bjBet[i];
        BJBet::InsuranceState insState = pb->GetInsuranceState();

		if( insState == BJBet::OFFERED )
		{
			if (dealer)
			{
                if( !IsLateInsurance() )
                {
					pb->SetInsuranceState(BJBet::DECLINED);	//NOT TAKEN
                	conn->trackSaveGame();
                }
			}
		}
		else if( insState == BJBet::TAKEN )
		{
			pb->HandlePlayerInsurancePayout();		
		}
    }

}

// used when restoring saved game
void BJGame::initializeSideBet(BJSavedGame* saved)
{    
    m_pSideBet = NULL;

    switch(conn->GetMachId())
    {
        case PERFECT_PAIRS:
        {
            // kharmon.  02-13-07.  #16101.  If this is perfect pairs, we have this extra optional 
            // side bet that the player can make.  We'll put all the logic for dealing with that into his
            // PerfectPairSideBet class.
            m_pSideBet = new PerfectPairSideBet(&stdMgr, conn, saved);
            break;
        }       
    }
}


// used for new game.
void BJGame::initializeSideBet(int32 anSideBets[BJMAXBETS])
{    
    m_pSideBet = NULL;

    switch(conn->GetMachId())
    {
        case PERFECT_PAIRS:
        {
            // kharmon.  02-13-07.  #16101.  If this is perfect pairs, we have this extra optional 
            // side bet that the player can make.  We'll put all the logic for dealing with that into his
            // PerfectPairSideBet class.
            m_pSideBet = new PerfectPairSideBet(&stdMgr, conn, m_maxBJBets, anSideBets);
            break;
        }       
    }
}
